# -*- coding: utf-8 -*-

"""
@author: Laowang
@contact: QQ:1125564921
@Created on: 2022/7/7
@Remark: 
"""

"""
deprecated
"""

import grpc
import logging
from collections import defaultdict
from copy import deepcopy
import threading
import hprose
from itertools import groupby
from operator import itemgetter
from typing import Callable, TypeVar, Mapping, Tuple
import types
import inspect
import json
from collections import UserDict
from framework_strategy.gopy.dao.orderline import StrategyOrderLine  # NOQA
from framework_strategy.gopy.base.gateway import BaseGateway  # NOQA
from framework_strategy.gopy.gateways.object import *  # NOQA
from framework_strategy.gopy.driver.core import OPRedis  # NOQA


# ----------constant------------

no_value = object()
TIMEOUT = 10

_KT = TypeVar("_KT")  # Key type.
_VT = TypeVar("_VT")  # Value type.
DESCRIPTION = "describe"
doc = ""

# ----------constant------------

__rpc__method__ = [
    "get_server_time",
    "describe",
    "get_account_info",
    "get_all_market_config",
    "get_market_kline_data",
    "get_market_ticker_data",
    "get_market_tickers_data",
    "get_market_depth_data",
    "get_market_trades",
    "get_account_balance",
    "get_account_fund",
    "send_order",
    "send_orders",
    "cancel_order",
    "cancel_orders",
    "get_account_order",
    "get_open_order",
    "get_orders",
    "get_mine_orders",
]

# prapare_muti
no_value = []


def response_data_handle(handle, response):

    try:
        res = handle()
    except Exception as e:
        pass
        # try:
        #    logging.error("[response_data_handle]", e)
        # except TypeError:
        #     logging.error("[response_data_handle - TypError]", e)
        # logging.error("[response_data_handle]", e,
        #             "response >>>", response.data)
        return no_value
    return res


class MutiMethodMixins:
    """ """
    EXPIRE = 500
    __slots__ = ()
    LOCK = threading.Lock

    def lru_cache(self, name: str) -> Any:
        """ """
        return self.get_cache(name)

    def exist(self, name: str) -> bool:
        return self.cache.exists(name)

    def set_cache(self, name: str, value=""):
        """ """
        if name == DESCRIPTION:
            self.cache.set(name, value)
            return

        if not self.exist(name):
            self.cache.setex(name, self.EXPIRE, value)

    def get_cache(self, name: str) -> Any:
        """ """
        value = self.cache.get(name)

        if isinstance(value, bytes):
            value = value.decode("utf-8")
        if value == "" and not value:
            return
        if name != DESCRIPTION:
            try:
                value = json.loads(value)
            except:
                # print("[get_cache] ", value, type(value), name)
                return
        return value


class MutiMethodMeta(MutiMethodMixins):
    """ TODO """

    def __init__(self, name: str, func: Callable[[Any], Any]) -> None:
        """ """
        self._cache = OPRedis()
        self.__methods = dict()
        self._name = name
        self._func = func

    @property
    def cache(self):
        """ """
        return self._cache

    def __call__(self, *args: Any, **kwds: Any) -> Any:
        """ """
        kw = dict()
        _, sig = self.__methods[self._name]
        args = sig.bind_partial(*args, **kwds)
        parameters = [x for x in args.args]

        if len(args.kwargs):
            parameters += list(args.kwargs.values())

        for k, v in zip(sig.parameters.keys(), parameters):
            kw[k] = v

        cache = self.lru_cache(self._name)
        if cache and cache != "":
            if not isinstance(cache, Dict):
                return cache
            else:
                kw = cache
            kw["self"] = args.args[0]
        return self._func(**kw)

    def register(self, func: Callable[[Any], Any]) -> None:
        """ """
        global doc
        sig = inspect.signature(func)
        doc += self._func.__doc__
        self.set_cache(DESCRIPTION, doc)
        if not self._name == DESCRIPTION:
            self.set_cache(self._name)
        self.__methods[self._name] = func, sig

    def __get__(self, instance: object, owner):
        """ """
        if not instance:
            return self
        return types.MethodType(self, instance)

    @property
    def __name__(self) -> str:
        """ """
        return self._name


class MultiDict(UserDict):
    """ """

    def __setitem__(self, key: _KT, item: _VT) -> None:
        """ """
        if key in __rpc__method__:
            print("dddddddddd", key, item)
            mutiMethodMeta = MutiMethodMeta(key, item)
            mutiMethodMeta.register(item)
            return super().__setitem__(key, mutiMethodMeta)
        return super().__setitem__(key, item)


class MultipleMeta(type):
    '''
    Metaclass that allows multiple dispatch of methods
    '''
    def __new__(cls, clsname, bases, clsdict):
        return type.__new__(cls, clsname, bases, dict(clsdict))

    @classmethod
    def __prepare__(metacls, __name: str, __bases: Tuple[type, Any], **kwds: Any) -> Mapping[str, object]:
        return MultiDict()


class HproseClientStructure(BaseGateway, metaclass=MultipleMeta):
    """ """


class HproseClient(BaseGateway):
    """ """

    exchanges = ["HproseClient"]

    def __init__(self, main_engine, event_engine, gateway_name: str = None, proxy=None, loop=None) -> None:
        gateway_name = gateway_name or "hprose-server"
        super().__init__(main_engine, event_engine, gateway_name, proxy)
        self.client = main_engine.client
        self.loop=loop
        self.start_server()

    def close(self):
        print("[HproseClient] shutdown ...")
        return

    def _process(self):
        self.server.start()

    def start_server(self):
        self._thread = threading.Thread(target=self._process, daemon=True)
        self.server = server = hprose.HttpServer(port=9898)
        server.debug = True
        for method in __rpc__method__:
            if not hasattr(self, method):
                continue
            func = getattr(self, method)
            server.addFunction(func)
        self._thread.start()

    async def get_server_time(self, kw: dict = no_value):
        """
        Method : get_server_time(self, kw: dict = no_value)
        ----
        Explain: Gets the server time  
        :: param  
            * kw: The parameter is empty by default  
            * kw default :{"exchange":{}}  
            The return value of 
            * kw for the default value is XT server time  
        ----  
        Usage:  
            * kw :{"XT":{}}, query XT service time,  
                {"BINANCE":{}}, query the service time of BINANCE  
            * kw: query the server time of multiple platforms, {"XT":{},"BINANCE":{}} 
        ----
        """
        def _get_server_time(source="XT"):
            e = ServerTimeData(_source=source)
            self.client.get_server_time(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = TIMEOUT

        if kw is no_value:
            _get_server_time()
        else:
            timeout = int(kw.pop("timeout", timeout))
            for k, _ in kw.items():
                _get_server_time(k.upper())
        for es in event:
            response = es.get_result(timeout=timeout)
            res_data[es.exchange] = response_data_handle(
                lambda data=response.data: data, response)

        return res_data

    async def get_account_info(self, kw: dict = no_value):
        """
        Method: get_account_info(self, kw: dict = no_value)
        ----
        Explain: Obtain platform account type information  
        :: param  
            * kw: The parameter is empty by default  
            * kw default :{"exchange":{}}  
        The return value of * kw for the default value is account info of XT  
        ----  
        Usage:  
            * kw :{"XT":{}}, query XT service time,  
                {"BINANCE":{}}, query the service time of BINANCE  
            * kw: query the server time of multiple platforms, {"XT":{},"BINANCE":{}} 
        """

        def _get_account_info(source="XT"):
            e = AccountInfoData(_source=source)
            self.client.get_account(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = TIMEOUT

        if kw is no_value:
            _get_account_info()
        else:
            timeout = int(kw.pop("timeout", timeout))
            for k, _ in kw.items():
                _get_account_info(k.upper())
        for es in event:
            response = es.get_result(timeout=timeout)
            if not response.status:
                raise grpc.RpcError()
            res_data[es.exchange] = response_data_handle(
                lambda data=response.data: data, response)

        return res_data

    async def get_all_market_config(self, kw: dict = no_value):
        """
        Method: get_all_market_config(self, kw: dict = no_value)
        ----
        Explain: Obtain platform market type configuration information  
        :: param  
            * kw: The parameter is empty by default  
            * kw default :{"exchange":{}}  
        The return value of * kw for the default value is all market configurations of XT
        ----  
        Usage:  
            * kw :{"XT":{}}, Example Query all market configurations of XT,  
            {"BINANCE":{}}, Example Query all market configurations of BINANCE  
            * kw: Batch querying the configurations of all markets on multiple platforms, {"XT":{},"BINANCE":{}} 
        """

        def _get_all_market_config(source="XT"):
            e = MarketsConfigData(_source=source)
            self.client.get_all_market_config(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = TIMEOUT

        if kw is no_value:
            _get_all_market_config()
        else:
            timeout = int(kw.pop("timeout", timeout))
            for k, _ in kw.items():
                _get_all_market_config(k.upper())
        for es in event:
            response = es.get_result(timeout=timeout)
            res_data[es.exchange] = response_data_handle(
                lambda data=response.data: data, response)

        return res_data

    async def get_market_kline_data(self, kw: dict):
        """
        Method: get_market_kline_data(self, kw: dict)
        ----
        Explain: Obtain k line information corresponding to the market  
        ::param  
            * kw: The name of the corresponding market is needed to check the information of K line.  
            * data : [1657186642, 20496.23, 20496.23, 20496.23, 20496.23, 0]
            * explain ['OpenTime', 'OpenPrice', 'High', 'Low', 'Close', 'Volume']
        ----  
        Usage:  
            * kw : {
                "XT":[{
                "market":"btc_usdt",                    Mandatory parameter
                "type":"1min",                          Mandatory parameter
                "since":time.time()                     Mandatory parameter
                    },]
                }  
            * kw : {
                "XT":[{
                    "market":"btc_usdt",                Mandatory parameter
                    "type":"1min",                      Mandatory parameter
                    "since":time.time()                 Mandatory parameter
                    }], 
                "BINANCE":[{
                    "market":"btc_usdt",                Mandatory parameter
                    "type":"1min",                      Mandatory parameter
                    "since":time.time()                 Mandatory parameter
                    }]
                }  
        ::param type: time range, for example, 1min, 5min, 15min, 1h, 1d, 1w  
        ::param since: indicates the time source. The type is integer 

        """

        def _get_market_kline_data(source="XT", **kw):
            e = MarketKlineData(_source=source, **kw)
            self.client.get_klines(e)
            event.append(e)

        def _get_kline_prepare(ex, kwargs):
            for kline in kwargs:
                yield kline

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        market = dict()

        for k, v in kw.items():
            for data in _get_kline_prepare(k, v):
                _get_market_kline_data(k.upper(), **data)

        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            market[es.market] = response_data_handle(f, response)
            res_data[es.exchange].append(market)
        return res_data

    async def get_market_ticker_data(self, kw: dict):
        """
        Method: get_market_ticker_data(self, kw: dict)
        ----
        Explain: Get the ticker information corresponding to the market
        ::param 
            * kw :  Need to pass in the name of the corresponding market,
            * data : [21979.0, 0, 21984.0, 0, 21981.0]
            * explain ['bidPrice', 'bidQty', 'askPrice', 'askQty', 'lastPrice']
            ----
            Usage:
            * kw : {
                "XT":{
                    "market":"btc_usdt"                 Mandatory parameter
                    }
                }
            * kw : {
                "XT":{
                    "market":"btc_usdt"                 Mandatory parameter
                    }, 
                "BINANCE":{
                    "market":"btc_usdt"                 Mandatory parameter
                    }
                }
        """

        def _get_market_ticker_data(source="XT", **kw):
            e = MarketTickerData(_source=source, **kw)
            self.client.get_ticker(e)
            event.append(e)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        market = dict()

        for k, v in kw.items():
            _get_market_ticker_data(k.upper(), **v)
        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return data["data"]._asdict()
            market[es.market] = response_data_handle(f, response)
            res_data[es.exchange].append(market)
        return res_data

    async def get_market_tickers_data(self, kw: dict = no_value):
        """
        Method: get_market_tickers_data(self, kw: dict)
        ----
        Get information about tickers in the market  
        ::param  
            * kw: Need to pass in the name of the corresponding market, default does not pass in all markets,  
            * data: {
                'XT': {
                    'data': {
                        'xwc_eth': [7.59e-05, 0, 7.682e-05, 0, 7.629e-05],
                        }
                    }
                }
            * explain  { 
                Exchange: {
                    'data': {
                        market : [askPrice, askQty, bidPrice, bidQty, lastPrice],
                        }
                    }
                }

            # XT does not support parameter passing  
            # binance Pass parameter type is list 
            The return value of * kw for the default value is all market tickers of XT
            ----
            Usage:
            * kw : {"XT":{}}
            * kw : {"XT":{}, "BINANCE":{"market":["btc_usdt", "eth_usdt"]}}
            * kw : {"XT":{}, "BINANCE":{}}
        """

        def _get_market_tickers_data(source="XT", v={}):

            if len(dict(v)) > 0:
                e = MarketTickersData(_source=source, **v)
            else:
                e = MarketTickersData(_source=source)
            self.client.get_tickers(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = TIMEOUT

        if kw is no_value:
            _get_market_tickers_data()
        else:
            timeout = int(kw.pop("timeout", TIMEOUT))
            for k, v in kw.items():
                _get_market_tickers_data(k.upper(), v)
        for es in event:
            response = es.get_result(timeout=timeout)
            res_data[es.exchange] = response_data_handle(
                lambda data=response.data: data, response)

        return res_data

    async def get_market_depth_data(self, kw: dict):
        """
        Method: get_market_dpeth_data(self, kw: dict)
        ----
        Get depth information about the market  
        ::param  
            * kw: Need to pass in the name of the corresponding market, 
            ----
            Usage:
            * kw : {
                "XT":[{
                    "market":"btc_usdt"                 Mandatory parameter
                    }]
                }
            * kw : {
                "XT":[{
                    "market":"btc_usdt"                 Mandatory parameter
                    }], 
                "BINANCE":[{
                    "market":"btc_usdt"                 Mandatory parameter
                    }]
                }
        """

        def _get_market_depth_data(source="XT", **kw):
            e = MarketDepthData(_source=source, **kw)
            self.client.get_depth(e)
            event.append(e)

        def _get_depth_prepare(depths):
            for depth in depths:
                yield depth

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        market = dict()

        for k, v in kw.items():
            for data in _get_depth_prepare(v):
                _get_market_depth_data(k.upper(), **data)

        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return data["data"]._asdict()
            market[es.market] = response_data_handle(f, response)
            res_data[es.exchange].append(market)
        return res_data

    async def get_market_trades(self, kw: dict):
        """ 
        Method: get_market_trades(self, kw: dict)
        ----
        Access to market trading information
        ::param 
            * kw : You need to pass in the name of the corresponding market,
            ----
            Usage:
            * kw : {"XT":{"market":"btc_usdt"}}
            * kw : {"XT":{"market":"btc_usdt"}, "BINANCE":{"market":"btc_usdt"}}
        """
        def _get_market_trade_data(source="XT", **kw):
            e = MarketTradesData(_source=source, **kw)
            self.client.get_trades(e)
            event.append(e)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        market = dict()

        for k, v in kw.items():
            _get_market_trade_data(k.upper(), **v)
        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            market[es.market] = response_data_handle(f, response)
            res_data[es.exchange].append(market)
        return res_data

    async def get_account_balance(self, kw: dict):
        """
        Method: get_account_balance(self, kw: dict)
        ----
        Generate the signature and pass the required parameters to obtain the account fund information  
        ::param  
            * kw: public and private keys to be imported into the account. 
            ----
            Usage:
            * kw : {
                "XT":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy"
                    }
                }
            * kw : {
                "XT":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy"
                    }, 
                "BINANCE":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy"
                    }
                }
        ::param accesskey public key, Generate a signature. Mandatory parameter
        ::param secretkey secret key, Generate a signature. Mandatory parameter
        """

        def _get_account_balance(source="XT", **kw):
            e = AccountBalanceData(_source=source, **kw)
            self.client.get_balance(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = int(kw.pop("timeout", TIMEOUT))

        for k, v in kw.items():
            _get_account_balance(k.upper(), **v)
        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            res_data[es.exchange] = response_data_handle(f, response)
        return res_data

    async def get_account_fund(self, kw: dict):
        """
        Method: get_account_balance(self, kw: dict)
        ----
        Generate the signature and pass the required parameters to obtain the account fund information  
        ::param  
            * kw: public and private keys to be imported into the account. 
            ----
            Usage:
            * kw : {
                "XT":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy"
                    }
                }
            * kw : {
                "XT":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy"
                    }, 
                "BINANCE":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy"
                    }
                }
        ::param accesskey public key, Generate a signature. Mandatory parameter
        ::param secretkey secret key, Generate a signature. Mandatory parameter
        """

        def _get_account_fund(source="XT", **kw):
            e = AccountFundData(_source=source, **kw)
            self.client.get_balance(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = int(kw.pop("timeout", TIMEOUT))

        for k, v in kw.items():
            _get_account_fund(k.upper(), **v)
        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            res_data[es.exchange] = response_data_handle(f, response)
        return res_data

    async def send_order(self, kw: dict):
        """
        Method: send_order(self, kw: dict)
        ----
        The aggregation order
        ::param  
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {
                "XT":{
                    "market":"btc_usdt",
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "price":100, 
                    "type":1, 
                    "entrustType": 0, 
                    "number":10
                    },
                "uuid":"zzzzzzzzzzz"
                }
            * kw : {
                "XT":{
                    "market":"btc_usdt",
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "price":100, 
                    "type":1, 
                    "entrustType": 0,  
                    "number":10
                    },
                "BINANCE":{
                    "market":"btc_usdt",
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "price":100, 
                    "type":1, 
                    "entrustType": 0,  
                    "number":10
                    },
                "uuid":"zzzzzzzzzzz"
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param price Price                                 Mandatory parameter
        ::param uuid UUID                                   Mandatory parameter
        ::param type Side                                   Mandatory parameter
        ::param entrustType Type of order                   Mandatory parameter
        ::param number Trading volume                       Mandatory parameter
        """
        _drop = StrategyOrderLine.DROP

        def _send_order(source="XT", **kw):
            kw["_extra"] = deepcopy(kw)
            kw["_extra"]["uuid"] = uuid
            (kw.pop(_column, 0) for _column in _drop)
            e = AccountSendOrderData(_source=source, **kw)
            self.client.send_order(e)
            event.append(e)

        event = []
        res_data = dict()
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))

        for k, v in kw.items():
            _send_order(k.upper(), **v)
        for es in event:
            try:
                response = es.get_result(timeout=timeout)
                def f(data=response.data): return data["data"]._asdict()
                res_data[es.exchange] = response_data_handle(f, response)
            except Exception as e:
                print(e)
            # print(["response"], response)
            try:
                def f(data=response.data): return data["data"]._asdict()
                res_data[es.exchange] = response_data_handle(f, response)
            except TypeError as e:
                print(e)

        res_data["uuid"] = uuid
        return res_data

    async def send_orders(self, kw: dict):
        """
        Method: send_orders(self, kw: dict)
        ----
        Batch order
        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {        
                "XT":{
                    "uuid":"123123123e",
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "data": [
                        {"price":100, "type":1, "entrustType: "0", "number":10,"market":"btc_usdt"},
                        {"price":100, "type":1, "entrustType: "0", "number":10,"market":"btc_usdt"},
                        ]
                    },
                    "uuid":"zzzzzzzzzzz"

            * kw : {
                "XT":{
                    "uuid":"123123123e",
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "data": [
                            {"price":100, "type":1, "entrustType: "0", "number":10,"market":"btc_usdt"},
                            {"price":100, "type":1, "entrustType: "0", "number":10,"market":"btc_usdt"},
                        ]
                },
                "BINANCE":{
                    "uuid":"123123123e",
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "data": [
                            {"price":100, "type":1, "entrustType: "0", "number":10,"market":"btc_usdt"},
                            {"price":100, "type":1, "entrustType: "0", "number":10,"market":"btc_usdt"},
                        ]
                    },
                "uuid":"zzzzzzzzzzz"
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param price Price                                 Mandatory parameter
        ::param uuid UUID                                   Mandatory parameter
        ::param type Side                                   Mandatory parameter
        ::param entrustType Type of order                   Mandatory parameter
        ::param number Trading volume                       Mandatory parameter
        ::param data The order details                      Mandatory parameter
        """
        column = "market"
        _drop = StrategyOrderLine.DROP

        def _send_orders(source="XT", **kw):
            kw["_extra"] = kw
            kw["_extra"]["uuid"] = uuid
            e = AccountSendOrdersData(_source=source, **kw)
            self.client.send_orders(e)
            event.append(e)

        def _prepare_send_order(order):

            for market, items in groupby(order, key=itemgetter(column)):
                print("[market] ", market)
                orders = []
                for _orderline in items:
                    _orderline.pop(column)
                    orders.append(_orderline)
                yield orders, market

        def _send_order_mouted(kw):
            for k, v in kw.items():
                order = v["data"]
                for order_line, market in _prepare_send_order(order):
                    v["data"] = order_line
                    v["_extra"] = deepcopy(v)
                    v["market"] = market
                    (v.pop(_column, 0) for _column in _drop)
                    _send_orders(k.upper(), **v)

        event = []
        res_data = dict()
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))
        _send_order_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            if es.exchange in res_data:
                res_data[es.exchange] += response_data_handle(f, response)
            else:
                res_data[es.exchange] = response_data_handle(f, response)
        res_data["uuid"] = uuid
        return res_data

    async def cancel_order(self, kw: dict):
        """
        Method: cancel_order(self, kw: dict)
        ----
        Cancel order
        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {        
                "XT":[
                    {
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        data:[
                            {"id":6950626137221115904, "market":"btc_usdt"}
                        ]
                    },
                "uuid":"zzzzzzzzzzz"
                ]

            * kw : {
                "XT": {
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        data:[
                            {"id":6950626137221115904, "market":"btc_usdt"}
                        ]
                    },
                "BINANCE":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        data:[
                            {"id":6950626137221115904, "market":"btc_usdt"}
                        ]
                    },
                "uuid":"zzzzzzzzzzz"
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param uuid UUID                                   Mandatory parameter
        ::param id OrderId                                  Mandatory parameter
        ::param market                                      Mandatory parameter
        ::param data Multiple orders in multiple markets    Mandatory parameter
        ----
        Recommended
            * It is recommended that different markets and order ids use this interface to cancel orders.  
            * If there are multiple orders and the same market, 
            * it is best to use the bulk withdrawal interface to achieve this 
        """

        def _cancel_order(source="XT", **kw):
            kw["_extra"] = deepcopy(kw)
            kw["_extra"]["uuid"] = uuid

            e = AccountCancelOrderData(_source=source, **kw)
            self.client.cancel_order(e)
            event.append(e)

        def _cancel_order_mouted(kw):

            for k, v in kw.items():
                order = v["data"]
                kw_cp = deepcopy(kw)[k]
                kw_cp.pop("data")
                for o in order:
                    kw_cp.update(**o)
                    kw_cp.pop("uuid", 0)
                    _cancel_order(k.upper(), **kw_cp)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))

        _cancel_order_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return data["data"]._asdict()
            res_data[es.exchange].append(response_data_handle(f, response))

        res_data["uuid"] = uuid
        return res_data

    async def cancel_orders(self, kw: dict):
        """
        Method: cancel_orders(self, kw: dict)
        ----
        Batch cancel order
        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {        
                "XT":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "data": [
                        {
                            "market":"btc_usdt",
                            "data":["orderId1","orderId2"]
                        },
                        {
                            "market":"eth_usdt",
                            "data":["orderId1","orderId2"]
                        },
                    ]
                    },
                "uuid":"zzzzzzzzzzz"

            * kw : {
                "XT":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "data": [
                        {
                            "market":"btc_usdt",
                            "data":["orderId1","orderId2"]
                        },
                        {
                            "market":"eth_usdt",
                            "data":["orderId1","orderId2"]
                        },
                    ]
                },
                "BINANCE":{
                    "accesskey":"xxxxxx", 
                    "secretkey":"yyyyyyy", 
                    "data": [
                        {
                            "market":"btc_usdt",
                            "data":["orderId1","orderId2"]
                        },
                        {
                            "market":"eth_usdt",
                            "data":["orderId1","orderId2"]
                        },
                    ]
                    },
                "uuid":"zzzzzzzzzzz"
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param uuid UUID                                   Mandatory parameter
        ::param id OrderId                                  Mandatory parameter
        ::param market                                      Mandatory parameter
        ::param data Multiple OrderId in multiple markets    Mandatory parameter

        """

        def _cancel_orders(source="XT", **kw):
            kw["_extra"] = deepcopy(kw)
            kw["_extra"]["uuid"] = uuid

            e = AccountCancelOrdersData(_source=source, **kw)
            self.client.cancel_orders(e)
            event.append(e)

        def _cancel_orders_mouted(kw):

            for k, v in kw.items():
                orders = v["data"]
                kw_cp = deepcopy(kw)[k]
                kw_cp.pop("data")
                for o in orders:
                    kw_cp.update(**o)
                    _cancel_orders(k.upper(), **kw_cp)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))

        _cancel_orders_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            if es.exchange in res_data:
                def f(data=response.data): return [
                    d._asdict() for d in data["data"]]
                res_data[es.exchange] += response_data_handle(f, response)
            else:
                res_data[es.exchange] = response_data_handle(f, response)

        res_data["uuid"] = uuid
        return res_data

    async def get_account_order(self, kw: dict):
        """
        Method: get_account_order(self, kw: dict)
        ----
        Gets the status information for the account order

        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {        
                "XT":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "data": [
                            {
                                "market":"btc_usdt",
                                "data":["orderId1","orderId2"]
                            },
                            {
                                "market":"eth_usdt",
                                "data":["orderId1","orderId2"]
                            },
                        ],
                        "uuid":"zzzzzzzzzzz"

                    },

            * kw : {
                "XT":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "data": [
                            {
                                "market":"btc_usdt",
                                "data":["orderId1","orderId2"]
                            },
                            {
                                "market":"eth_usdt",
                                "data":["orderId1","orderId2"]
                            },
                        ],
                        "uuid":"zzzzzzzzzzz"

                    },
                "BINANCE":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "data": [
                            {
                                "market":"btc_usdt",
                                "data":["orderId1","orderId2"]
                            },
                            {
                                "market":"eth_usdt",
                                "data":["orderId1","orderId2"]
                            },
                        ],
                        "uuid":"zzzzzzzzzzz"
                    },
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param id OrderId                                  Mandatory parameter
        ::param market                                      Mandatory parameter
        ::param data Multiple OrderId in multiple markets   Mandatory parameter
        """
        def _account_order(source="XT", **kw):
            # kw["_extra"] = deepcopy(kw)
            # kw["_extra"]["uuid"] = uuid

            e = AccountOrderData(_source=source, **kw)
            self.client.get_order(e)
            event.append(e)

        def _generator_order(orders, kw_cp):

            for o in orders:
                kw_cp["market"] = o["market"]
                for oi in o["data"]:
                    kw_cp["id"] = oi
                    yield kw_cp

        def _account_order_mouted(kw):

            for k, v in kw.items():
                orders = v["data"]
                kw_cp = deepcopy(kw)[k]
                kw_cp.pop("data")

                for _kw_cp in _generator_order(orders, kw_cp):
                    _account_order(k.upper(), **_kw_cp)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))  # 非必传参数

        _account_order_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            def f(data=response.data): return data["data"]._asdict()
            res_data[es.exchange].append(response_data_handle(f, response))

        res_data["uuid"] = uuid
        return res_data

    async def get_open_order(self, kw: dict):
        """
        Method: get_account_order(self, kw: dict)
        ----
        Get outstanding orders
        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {        
                "XT":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "market":["btc_usdt","eth_usdt",...]
                    },
                    "uuid":"zzzzzzzzzzz",

            * kw : {
                "XT":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "market":["btc_usdt","eth_usdt",...]
                    },
                "BINANCE":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "market":["btc_usdt","eth_usdt",...]
                    },
                "uuid":"zzzzzzzzzzz"
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param market                                      Mandatory parameter
        """
        def _open_order(source="XT", **kw):
            # kw["_extra"] = deepcopy(kw)
            # kw["_extra"]["uuid"] = uuid

            e = AccountOpenOrderData(_source=source, **kw)
            self.client.get_open_order(e)
            event.append(e)

        def _generator_order(markets, kw_cp):

            for symbol in markets:
                kw_cp["market"] = symbol
                yield kw_cp

        def _open_order_mouted(kw):

            for k, v in kw.items():
                markets = v["market"]
                kw_cp = deepcopy(kw)[k]
                for _kw_cp in _generator_order(markets, kw_cp):
                    _open_order(k.upper(), **_kw_cp)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))  # 非必传参数

        _open_order_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            res_market = defaultdict(list)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            res_market[es.market].append(response_data_handle(f, response))
            res_data[es.exchange].append(res_market)
        res_data["uuid"] = uuid
        return res_data

    async def get_orders(self, kw: dict):
        """
        Method: get_account_order(self, kw: dict)
        ----
        Batch querying the status of orders
        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary
            ----
            Usage:
            * kw : {        
                "XT":{
                    "accesskey":"xxxxxx", "secretkey":"yyyyyyy", 
                    "data": [
                            {
                                "market":"btc_usdt",
                                "data":["orderId1","orderId2"]
                            },
                            {
                                "market":"eth_usdt",
                                "data":["orderId1","orderId2"]
                            },
                        ],
                    },
                "uuid":"zzzzzzzzzzz",

            * kw : {
                "XT":{
                    "accesskey":"xxxxxx", "secretkey":"yyyyyyy", 
                    "data": [
                            {
                                "market":"btc_usdt",
                                "data":["orderId1","orderId2"]
                            },
                            {
                                "market":"eth_usdt",
                                "data":["orderId1","orderId2"]
                            },
                        ],
                },
                "BINANCE":{"accesskey":"xxxxxx", "secretkey":"yyyyyyy", 
                    "data": [
                            {
                                "market":"btc_usdt",
                                "data":["orderId1","orderId2"]
                            },
                            {
                                "market":"eth_usdt",
                                "data":["orderId1","orderId2"]
                            },
                        ],
                    },
                "uuid":"zzzzzzzzzzz",
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param data Multiple OrderId in multiple markets   Mandatory parameter

        """
        def _account_order_status(source="XT", **kw):
            # kw["_extra"] = deepcopy(kw)
            # kw["_extra"]["uuid"] = uuid

            e = AccountOrdersData(_source=source, **kw)
            self.client.get_orders(e)
            event.append(e)

        def _generator_order(orders, kw_cp):

            for o in orders:
                kw_cp["market"] = o["market"]
                kw_cp["data"] = o["data"]
                yield kw_cp

        def _account_order_mouted(kw):

            for k, v in kw.items():
                orders = v["data"]
                kw_cp = deepcopy(kw)[k]
                kw_cp.pop("data")

                for _kw_cp in _generator_order(orders, kw_cp):
                    _account_order_status(k.upper(), **_kw_cp)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))  # 非必传参数

        _account_order_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            res_market = defaultdict(list)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            res_market[es.market] = response_data_handle(f, response)

            res_data[es.exchange].append(res_market)
        res_data["uuid"] = uuid
        return res_data

    async def get_mine_orders(self, kw: dict):
        """
        Method: get_mine_orders(self, kw: dict)
        ----
        Check the transactions on your account
        ::param 
            * kw: public and private keys to be imported into the account. 
            * kw: The type must be a dictionary,
            ----
            Usage:
            * kw : {        
                "XT":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "uuid":"zzzzzzzzzzz",

                        "market":["btc_usdt","eth_usdt",...]

                    },

            * kw : {
                "XT":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "uuid":"zzzzzzzzzzz",
                        "market":["btc_usdt","eth_usdt",...]
                    },
                "BINANCE":{
                        "accesskey":"xxxxxx", 
                        "secretkey":"yyyyyyy", 
                        "uuid":"zzzzzzzzzzz",
                        "market":["btc_usdt","eth_usdt",...]
                    },
                }
        ::param accesskey Public key, Generate a signature. Mandatory parameter
        ::param secretkey Secret key, Generate a signature. Mandatory parameter
        ::param data Multiple OrderId in multiple markets   Mandatory parameter
        ::param market                                      Mandatory parameter

        """

        def _mine_order(source="XT", **kw):
            # kw["_extra"] = deepcopy(kw)
            # kw["_extra"]["uuid"] = uuid

            e = AccountMineOrdersData(_source=source, **kw)
            self.client.get_mine_trades(e)
            event.append(e)

        def _generator_order(markets, kw_cp):

            for symbol in markets:
                kw_cp["market"] = symbol
                yield kw_cp

        def _mine_order_mouted(kw):

            for k, v in kw.items():
                markets = v["market"]
                kw_cp = deepcopy(kw)[k]
                for _kw_cp in _generator_order(markets, kw_cp):
                    _mine_order(k.upper(), **_kw_cp)

        event = []
        res_data = defaultdict(list)
        timeout = int(kw.pop("timeout", TIMEOUT))
        uuid = str(kw.pop("uuid", ""))  # 非必传参数

        _mine_order_mouted(kw)

        for es in event:
            response = es.get_result(timeout=timeout)
            res_market = defaultdict(list)
            def f(data=response.data): return [
                d._asdict() for d in data["data"]]
            res_market[es.market] = response_data_handle(f, response)
            res_data[es.exchange].append(res_market)
        res_data["uuid"] = uuid
        return res_data

    async def describe(self, kw=None):
        """
        ************************
        * Class : HproseClient * 
        ************************
        ----
        Method : describe()
        ----
        Explain: Gets the functions that can be called and instructions for using them
        ----
        Note: Currently the return values are synchronous, 
        meaning that if multiple platform distributed operations are passed in, 
        they will wait for both to complete and return  
        By default, "kw" supports passing timeout to control the timeout 
        """
        try:
            desc = self.get_cache(DESCRIPTION)
        except:
            desc = None
        if desc:
            return desc

        def _filter(func):
            try:
                return func.__doc__
            except TypeError:
                return ""
        desc = [_filter(getattr(self, x)) for x in dir(
            self) if not x.startswith("_") and callable(getattr(self, x))]
        desc = [x for x in desc if x]
        br = "{:>8s}{}".format("", "-" * 80)
        desc_str = "\n{}".format(br).join(desc)
        return desc_str
